#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>

struct packet
{
    int len; //包体的实际字节数
    char buf[1024];
};

//读取确切的字节数count的readn方法
ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count; //剩余的字节数
    size_t nread;         //已经读取的字节数

    char *bufp = (char *)buf;
    //因为不能够保证一次能够读到多少字节，所以循环读取，直到读完
    while (nleft > 0)
    {
        if ((nread = read(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR) //被信号中断，不认为出错
            {
                continue;
            }
            else
                return -1;
        }
        else if (nread == 0)
        {
            return count - nleft; //已经读取的字节数
        }

        bufp += nread;
        nleft -= nread;
    }
    return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    size_t nwritten;

    char *bufp = (char *)buf;

    while (nleft > 0)
    {
        if ((nwritten = write(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR) //被信号中断，不认为出错
            {
                continue;
            }
            else
                return -1;
        }
        else if (nwritten == 0)
        {
            continue;
        }
        bufp += nwritten;
        nleft -= nwritten;
    }
    return count;
}
int main()
{
    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    if (listen < 0)
    {
        perror("socket:");
        exit(1);
    }

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(5188);
    //设置地址可以重复利用
    int on = 1;
    if ((setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
    {
        perror("setsockopt ");
        exit(1);
    }
    int ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (ret < 0)
    {
        perror("bind");
        exit(1);
    }

    ret = listen(listenfd, SOMAXCONN);
    if (ret < 0)
    {
        perror("listen");
        exit(1);
    }
    struct sockaddr_in peeraddr;
    socklen_t peeraddrlen = sizeof(peeraddr);
    while (1)
    {
        int connfd = accept(listenfd, (struct sockaddr *)&peeraddr, &peeraddrlen);
        if (connfd < 0)
        {
            perror("accept");
            exit(1);
        }
        printf("ip:%s port:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

        pid_t pid = fork(); //创建子进程
        if (pid < 0)
        {
            perror("fork");
            exit(1);
        }
        else if (pid == 0) //子进程处理通信过程
        {
            close(listenfd); //子进程会复制父进程的listened，子进程不需要使用，关闭它即可

            struct packet recvbuf;
            int n;
            while (1)
            {
                memset(&recvbuf, 0, sizeof(recvbuf));
                int sz = readn(connfd, &recvbuf.len, 4); //服务器接收客户端发来的数据,先接收包体的长度
                if (sz < -1)
                {
                    perror("readn");
                    exit(1);
                }
                else if (sz < 4)
                {
                    printf("peer close\n");
                    break;
                }
                //服务器接收客户端发来的数据,后接收包体
                n = ntohl(recvbuf.len);
                sz = readn(connfd, recvbuf.buf, n);
                if (sz < -1)
                {
                    perror("readn");
                    exit(1);
                }
                else if (sz < n)
                {
                    printf("peer close\n");
                    break;
                }
                printf("service accept client: ");
                fputs(recvbuf.buf, stdout); //服务器打印客户端发来的数据

                write(connfd, &recvbuf, 4+n); //服务端回射给客户端
            }
            exit(1); //客户端关闭，就会跳出到这行,该为客户端服务的子进程就没有存在的价值了
        }
        else //父进程
        {
            close(connfd);
        }
    }

    return 0;
}